package com.szholly.plug.safe.security;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;

import com.szholly.utils.common.log.LogFactory;
import com.szholly.utils.session.CurrentSessionHelper;
import com.szholly.utils.session.SessionFactory;
import com.szholly.utils.session.provider.ISessionProvider;
import com.szholly.utils.spring.SpringBeanFactory;
import com.szholly.utils.util.DateUtils;
import com.szholly.plug.safe.entity.LogEntity;
import com.szholly.plug.safe.entity.LogService;
import com.szholly.plug.safe.entity.LogTypeDic;
import com.szholly.plug.safe.entity.role.RoleInitHelper;
import com.szholly.plug.safe.entity.user.UserEntity;
import com.szholly.plug.safe.login.LoginParameter;

/**
 * 自定义访问决策器
 */
public class CustomAccessDecisionManager implements AccessDecisionManager {

	private static Logger logger = Logger.getLogger(CustomAccessDecisionManager.class);
	
	/**
	 * 权限资源决策类 Authentication 为当前用户评证 object 为当前请求的资源，类型为 URL 或 类方法
	 * Collection<ConfigAttribute> 为当前资源的所需角色
	 */
	public void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException, InsufficientAuthenticationException {

		// 方法调用
		if (object instanceof ReflectiveMethodInvocation) {
			ReflectiveMethodInvocation rmi = (ReflectiveMethodInvocation) object;
			Method md = rmi.getMethod();
			String resource = md.getDeclaringClass().getName() + "."
					+ md.getName();
			methodResourceDecide(resource, authentication);
		} else if (object instanceof FilterInvocation) {
			// URL调用
			FilterInvocation fi = (FilterInvocation) object;
			String resource = fi.getRequestUrl();
			urlResourceDecide(resource, authentication);
		}
	}

	/**
	 * 决策资源
	 * 
	 * @param resource
	 * @param authentication
	 */
	@SuppressWarnings("unchecked")
	private void urlResourceDecide(String resource,
			Authentication authentication) {

		ISessionProvider session = SessionFactory.getSession();
		UserEntity userEntity = (UserEntity) session.getObject(ISessionProvider.UserEntity);
		if (userEntity == null) {
			userEntity = initUserRole(authentication);
		}

		if (userEntity == null) {
			logger.info("urlResourceDecide 未知用户请求资源：" + resource + "失败！");
			throw new AccessDeniedException("访问权限不够！");
		}

		String userName = userEntity.getUserName();
		
		if(userEntity.getIsSuperAdmin()){
			logger.info("urlResourceDecide 用户:" + userName + "请求资源：" + resource + "通过！");
			logFunction(userEntity, "请求资源：" + resource + "通过！");
		} else {
			List<String> canAccessUrl = (List<String>) SessionFactory.getSession()
					.getObject(ISessionProvider.FunctionUrl);
			if (canAccessUrl == null) {
				logger.info("urlResourceDecide 用户:" + userName + "所授权的资源为空。请求资源：" + resource + "失败！");
				throw new AccessDeniedException("访问权限不够！");
			} else {
				String resourceUrl = resource.trim().toLowerCase();
				int i = resourceUrl.indexOf("?");
				if(i>0){
					resourceUrl = resourceUrl.substring(0, i);
				}
				
				if (canAccessUrl.contains("*") || canAccessUrl.contains(resourceUrl)) {
					logger.info("urlResourceDecide 用户:" + userName + "请求资源：" + resource + "通过！");
					logFunction(userEntity, "请求资源：" + resource + "通过！");
					return;
				} else {
					logger.info("urlResourceDecide 用户:" + userName + "请求资源：" + resource + "失败！");
					throw new AccessDeniedException("访问权限不够！");
				}
			}
		}
	}

	public static UserEntity initUserRole(Authentication authentication) {
		UserEntity userEntity = null;
		Object obj = authentication.getPrincipal();
		if (obj instanceof CustomUserDetails) {
			CustomUserDetails user = (CustomUserDetails) obj;
			LoginParameter loginParameter = (LoginParameter) SpringBeanFactory
					.getBean("loginParameter");
			if(loginParameter.isOnlyUser()){
				CurrentSessionHelper.optionSession(user.getUserEntity()
						.getUserID(), CurrentSessionHelper
						.getCurrentSession());
			}
			RoleInitHelper.getRoles(user.getUserEntity());
			userEntity = (UserEntity) SessionFactory.getSession()
					.getObject(ISessionProvider.UserEntity);
			// 记录日志
			logLogin(userEntity);
		}
		return userEntity;
	}
	
	private void logFunction(UserEntity userEntity, String desc) {
		if(!LogFactory.getlogPara().isLogFuntion()){
			return;
		}
		ISessionProvider session = SessionFactory.getSession();
		LogEntity log = new LogEntity();
		if (session != null) {
			log.setIP(session.getIp());
		}
		log.setLogDate(DateUtils.convertDateTimeToString(null));
		log.setLogDesc(desc);
		log.setLogType(LogTypeDic.Function);
		if (userEntity != null) {
			log.setUserName(userEntity.getUserRealName());
		}
		LogService.getSingleRef().SaveEntityInThread(log);
	}

	private static void logLogin(UserEntity userEntity) {
		if(!LogFactory.getlogPara().isLogIn()){
			return;
		}
		
		// 用户登录日志
		ISessionProvider session = SessionFactory.getSession();
		LogEntity log = new LogEntity();
		if(session!=null){
			log.setIP(session.getIp());
		}
		log.setLogDate(DateUtils.convertDateTimeToString(null));
		log.setLogDesc("登录成功");
		log.setLogType(LogTypeDic.LogIn);
		log.setUserName(userEntity.getUserRealName());
		LogService.getSingleRef().SaveEntityInThread(log);
	}

	/**
	 * 决策资源
	 * 
	 * @param resource
	 * @param authentication
	 */
	private void methodResourceDecide(String resource,
			Authentication authentication) {
		logger.info("urlResourceDecide 未知用户请求资源：" + resource + "失败！");
		throw new AccessDeniedException("访问权限不够！");
	}

	public boolean supports(ConfigAttribute attribute) {
		return true;
	}

	public boolean supports(Class<?> clazz) {
		return true;
	}
}
